/* Copyright (c) 2010, Carl Burch. License information is located in the
 * com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */

package com.cburch.logisim.gui.opts;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.Collections;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.ScrollPaneConstants;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;

import com.cburch.logisim.file.MouseMappings;
import com.cburch.logisim.gui.generic.AttrTable;
import com.cburch.logisim.gui.generic.AttrTableModel;
import com.cburch.logisim.gui.generic.ProjectExplorer;
import com.cburch.logisim.gui.generic.ProjectExplorerEvent;
import com.cburch.logisim.gui.generic.ProjectExplorerListener;
import com.cburch.logisim.gui.generic.ProjectExplorerToolNode;
import com.cburch.logisim.gui.main.AttrTableToolModel;
import com.cburch.logisim.proj.Project;
import com.cburch.logisim.tools.AddTool;
import com.cburch.logisim.tools.Tool;
import com.cburch.logisim.util.InputEventUtil;
import static com.cburch.logisim.util.LocaleString.*;

@SuppressWarnings("serial")
class MouseOptions extends OptionsPanel {
    private class AddArea extends JPanel {

        public AddArea() {
            setPreferredSize(new Dimension(75, 60));
            setMinimumSize(new Dimension(75, 60));
            setBorder(BorderFactory.createCompoundBorder(
                    BorderFactory.createEmptyBorder(10, 10, 10, 10),
                    BorderFactory.createEtchedBorder()));
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Dimension sz = getSize();
            g.setFont(remove.getFont());
            String label1;
            String label2;
            if (curTool == null) {
                g.setColor(Color.GRAY);
                label1 = getFromLocale("mouseMapNone");
                label2 = null;
            } else {
                g.setColor(Color.BLACK);
                label1 = getFromLocale("mouseMapText");
                label2 = getFromLocale("mouseMapText2", curTool.getDisplayName());
            }
            FontMetrics fm = g.getFontMetrics();
            int x1 = (sz.width - fm.stringWidth(label1)) / 2;
            if (label2 == null) {
                int y = Math.max(0, (sz.height - fm.getHeight()) / 2 + fm.getAscent() - 2);
                g.drawString(label1, x1, y);
            } else {
                int x2 = (sz.width - fm.stringWidth(label2)) / 2;
                int y = Math.max(0, (sz.height - 2 * fm.getHeight()) / 2 + fm.getAscent() - 2);
                g.drawString(label1, x1, y);
                y += fm.getHeight();
                g.drawString(label2, x2, y);
            }
        }
    }

    private class MyListener
            implements ActionListener, MouseListener, ListSelectionListener,
                MouseMappings.MouseMappingsListener, ProjectExplorerListener {
        //
        // ActionListener method
        //
        @Override
        public void actionPerformed(ActionEvent e) {
            Object src = e.getSource();
            if (src == remove) {
                int row = mappings.getSelectedRow();
                getProject().doAction(OptionsActions.removeMapping(getOptions().getMouseMappings(),
                    model.getKey(row)));
                row = Math.min(row, model.getRowCount() - 1);
                if (row >= 0) setSelectedRow(row);
            }
        }

        //
        // MouseListener methods
        //
        @Override
        public void mouseClicked(MouseEvent e) { }
        @Override
        public void mouseEntered(MouseEvent e) { }
        @Override
        public void mouseExited(MouseEvent e) { }
        @Override
        public void mousePressed(MouseEvent e) {
            if (e.getSource() == addArea && curTool != null) {
                Tool t = curTool.cloneTool();
                Integer mods = Integer.valueOf(e.getModifiersEx());
                getProject().doAction(OptionsActions.setMapping(getOptions().getMouseMappings(), mods, t));
                setSelectedRow(model.getRow(mods));
            }
        }
        @Override
        public void mouseReleased(MouseEvent e) { }

        //
        // ListSelectionListener method
        //
        @Override
        public void valueChanged(ListSelectionEvent e) {
            int row = mappings.getSelectedRow();
            if (row < 0) {
                remove.setEnabled(false);
                attrTable.setAttrTableModel(null);
            } else {
                remove.setEnabled(true);
                Tool tool = model.getTool(row);
                Project proj = getProject();
                AttrTableModel model;
                if (tool.getAttributeSet() == null) {
                    model = null;
                } else {
                    model = new AttrTableToolModel(proj, tool);
                }
                attrTable.setAttrTableModel(model);
            }
        }

        //
        // MouseMappingsListener method
        //
        @Override
        public void mouseMappingsChanged() {
            model.fireTableStructureChanged();
        }

        //
        // Explorer.Listener methods
        //
        @Override
        public void selectionChanged(ProjectExplorerEvent event) {
            Object target = event.getTarget();
            if (target instanceof ProjectExplorerToolNode) {
                Tool tool = ((ProjectExplorerToolNode) target).getValue();
                setCurrentTool(tool);
            } else {
                setCurrentTool(null);
            }
        }

        @Override
        public void doubleClicked(ProjectExplorerEvent event) { }
        @Override
        public void moveRequested(ProjectExplorerEvent event, AddTool dragged, AddTool target) { }
        @Override
        public void deleteRequested(ProjectExplorerEvent event) { }
        @Override
        public JPopupMenu menuRequested(ProjectExplorerEvent event) { return null; }
    }

    private class MappingsModel extends AbstractTableModel {
        ArrayList<Integer> cur_keys;

        MappingsModel() {
            fireTableStructureChanged();
        }

        // AbstractTableModel methods
        @Override
        public void fireTableStructureChanged() {
            cur_keys = new ArrayList<Integer>(getOptions().getMouseMappings().getMappedModifiers());
            Collections.sort(cur_keys);
            super.fireTableStructureChanged();
        }

        @Override
        public int getRowCount() {
            return cur_keys.size();
        }

        @Override
        public int getColumnCount() {
            return 2;
        }

        @Override
        public Object getValueAt(int row, int column) {
            Integer key = cur_keys.get(row);
            if (column == 0) {
                return InputEventUtil.toDisplayString(key.intValue());
            } else {
                Tool tool = getOptions().getMouseMappings().getToolFor(key);
                return tool.getDisplayName();
            }
        }

        // other methods
        Integer getKey(int row) {
            return cur_keys.get(row);
        }

        Tool getTool(int row) {
            if (row < 0 || row >= cur_keys.size()) return null;
            Integer key = cur_keys.get(row);
            return getOptions().getMouseMappings().getToolFor(key.intValue());
        }

        int getRow(Integer mods) {
            int row = Collections.binarySearch(cur_keys, mods);
            if (row < 0) row = -(row + 1);
            return row;
        }
    }

    private MyListener listener = new MyListener();
    private Tool curTool = null;
    private MappingsModel model;

    private ProjectExplorer explorer;
    private JPanel addArea = new AddArea();
    private JTable mappings = new JTable();
    private AttrTable attrTable;
    private JButton remove = new JButton();

    public MouseOptions(OptionsFrame window) {
        super(window, new GridLayout(1, 3));

        explorer = new ProjectExplorer(getProject());
        explorer.setListener(listener);

        // Area for adding mappings
        addArea.addMouseListener(listener);

        // Area for viewing current mappings
        model = new MappingsModel();
        mappings.setTableHeader(null);
        mappings.setModel(model);
        mappings.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        mappings.getSelectionModel().addListSelectionListener(listener);
        mappings.clearSelection();
        JScrollPane mapPane = new JScrollPane(mappings);

        // Button for removing current mapping
        JPanel removeArea = new JPanel();
        remove.addActionListener(listener);
        remove.setEnabled(false);
        removeArea.add(remove);

        // Area for viewing/changing attributes
        attrTable = new AttrTable(getOptionsFrame());

        GridBagLayout gridbag = new GridBagLayout();
        GridBagConstraints gbc = new GridBagConstraints();
        setLayout(gridbag);
        gbc.weightx = 1.0;
        gbc.weighty = 1.0;
        gbc.gridheight = 4;
        gbc.fill = GridBagConstraints.BOTH;
        JScrollPane explorerPane = new JScrollPane(explorer,
                ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
                ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        gridbag.setConstraints(explorerPane, gbc);
        add(explorerPane);
        gbc.weightx = 0.0;
        JPanel gap = new JPanel();
        gap.setPreferredSize(new Dimension(10, 10));
        gridbag.setConstraints(gap, gbc); add(gap);
        gbc.weightx = 1.0;
        gbc.gridheight = 1;
        gbc.gridx = 2;
        gbc.gridy = GridBagConstraints.RELATIVE;
        gbc.weighty = 0.0; gridbag.setConstraints(addArea, gbc); add(addArea);
        gbc.weighty = 1.0; gridbag.setConstraints(mapPane, gbc); add(mapPane);
        gbc.weighty = 0.0; gridbag.setConstraints(removeArea, gbc); add(removeArea);
        gbc.weighty = 1.0; gridbag.setConstraints(attrTable, gbc); add(attrTable);

        getOptions().getMouseMappings().addMouseMappingsListener(listener);
        setCurrentTool(null);
    }

    @Override
    public String getTitle() {
        return getFromLocale("mouseTitle");
    }

    @Override
    public String getHelpText() {
        return getFromLocale("mouseHelp");
    }

    @Override
    public void localeChanged() {
        remove.setText(getFromLocale("mouseRemoveButton"));
        addArea.repaint();
    }

    private void setCurrentTool(Tool t) {
        curTool = t;
        localeChanged();
    }

    private void setSelectedRow(int row) {
        if (row < 0) row = 0;
        if (row >= model.getRowCount()) row = model.getRowCount() - 1;
        if (row >= 0) {
            mappings.getSelectionModel().setSelectionInterval(row, row);
        }
    }
}
